package wxpay

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"math/rand"
	"sort"
	"strings"
	"time"
)

// Params 微信支付参数
type Params struct {
	AppID     string `json:"appId"`
	TimeStamp string `json:"timeStamp"`
	NonceStr  string `json:"nonceStr"`
	Package   string `json:"package"`
	SignType  string `json:"signType"`
	PaySign   string `json:"paySign"`
}

// GenerateParams 生成支付参数
func GenerateParams(config *Config, orderNo string, totalFee int64, description string) (*Params, error) {
	// 生成随机字符串
	nonceStr := generateNonceStr()

	// 生成时间戳
	timestamp := fmt.Sprintf("%d", time.Now().Unix())

	// 构建签名参数
	signParams := map[string]string{
		"appid":            config.AppID,
		"mch_id":           config.MchID,
		"nonce_str":        nonceStr,
		"body":             description,
		"out_trade_no":     orderNo,
		"total_fee":        fmt.Sprintf("%d", totalFee),
		"spbill_create_ip": "127.0.0.1",
		"notify_url":       config.NotifyURL,
		"trade_type":       "JSAPI",
	}

	// 生成签名
	sign := generateSign(signParams, config.APIKey)

	// 构建支付参数
	params := &Params{
		AppID:     config.AppID,
		TimeStamp: timestamp,
		NonceStr:  nonceStr,
		Package:   fmt.Sprintf("prepay_id=%s", orderNo),
		SignType:  "MD5",
		PaySign:   sign,
	}

	return params, nil
}

// generateNonceStr 生成随机字符串
func generateNonceStr() string {
	const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, 32)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

// generateSign 生成签名
func generateSign(params map[string]string, key string) string {
	// 按键排序
	var keys []string
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 构建签名字符串
	var signStr strings.Builder
	for _, k := range keys {
		if params[k] != "" {
			signStr.WriteString(k)
			signStr.WriteString("=")
			signStr.WriteString(params[k])
			signStr.WriteString("&")
		}
	}
	signStr.WriteString("key=")
	signStr.WriteString(key)

	// MD5加密
	h := md5.New()
	h.Write([]byte(signStr.String()))
	return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
}
